module mculib.baremetal.volatile.register;

//import std.traits:EnumMembers,,isScalarType;
import std.traits:EnumMembers,staticMap;
import core.atomic:atomicLoad,atomicStore,casWeak;

import mculib.arm.bitband;
import mculib.baremetal.common:makeBitField,combineOr,bitFieldWidth;


/**
    寄存器存储泛用操作结构
    可通过别名操作位,
    可通过游标位操作位,
    可通过掩码操作位


    结构不应被实例化, 仅作为泛用操作结构使用.
Params:
    T = 寄存器类型,可接收类型 Byte, Word, DWord, QWord
    E = 位枚举类型,位描述类型

Bugs: 
    位枚举类型必须为位标志类型,否则无法通过别名操作位.
    以数组`[index]`,游标访问位,游标越界等问题由编程人员自行处理.
    以数组`[enum]`,枚举类型访问位,由编程人员自行确认枚举值的有效性
    以数组`[flag]`访问位时,无法访问已声明`flag`之外的位.

    当`Flag`有效时`enum`访问无效

*/
struct VolatileRegister(E,T = uint,size_t address=0,size_t resetValue = 0,Access access = Access.RW)
{

    enum T readMask = readAccess!E;             /// 读取时每个位的访问权限
    enum T writeMask = writeAccess!E;            /// 写时每个位的访问权限
    enum T rwMask = readMask & writeMask;        /// 可同时进行读写的位掩码
    enum size_t pAddress = address;             /// 访问地址
    enum size_t pResetValue = resetValue;        /// 重置值
    enum Access pAccess = access;               /// 访问权限
    enum bool isBitBandable = SupportBitBand && CheckBitBandable!address;

    static if(isBitBandable)
        import mculib.baremetal.volatile.bitband;
    else
        import mculib.baremetal.volatile.atomic;


    private{
        union
        {
            T _value;           // 标准值
            shared(T) _sharedValue; // 共享值
            volatile!T _register; // bitband 访问
        }
    }

    @disable this();

    @disable opCall(Args...)(Args args);

    @property T* ptr() => &_value;
    @property ref volatile!T rawRegister() => _register;
    
    /// 重载别名赋值
    @property
    void opDispatch(string name)(T v)
    if(
        __traits(hasMember,E,name)
    ){
        enum e = __traits(getMember, E, name);
        /// 判定位域可写
        static assert(e.isWriteable,"Invalid bitfield access:" ~ name ~ " is not writable");
        /// 判定位域宽度大于0 且 小于等于寄存器宽度
        static assert(e.width >0 && e.width <= T.sizeof * 8,"Invalid bitfield width:" ~ name);

        static if(e.width == 1)
        {
            this._register[e.offset] = (v != 0)?true:false;
        }else{
            // 断言有读取权限
            static assert(e.isReadable,"Invalid bitfield access:" ~ name ~ " is not readable");
            
            /*
            v <<= e.offset;
            this._register.maskWrite(e.fieldMask,v);
            */

            // 起始字节位置偏移 
            enum startOffset = (e.offset / (ubyte.sizeof * 8));
            // 读写位偏移
            enum rwOffset = (e.offset % (ubyte.sizeof * 8));
            // 读写掩码偏移
            enum rwMaskOffset = (e.fieldMask >>> (startOffset * ubyte.sizeof * 8));

            // 确定访问类型
            alias getType = getAvailableType!(rwOffset + e.width);
            /// 访问地址
            getType* nptr = cast(getType*)((&this._sharedValue) + startOffset);

            v <<= rwOffset;
            
            getType mset, mget = atomicLoad(*nptr);
            
            do{
                mset = (mget & ~rwMaskOffset) | (v & rwMaskOffset);
            }while (!casWeak(cast(shared)nptr, mget, mset));
        }
    }
    @property
    auto opDispatch(string name)() const
    if(
        __traits(hasMember,E,name)
    )
    {
        /// 判定位域可读
        enum e = __traits(getMember, E, name);
        static assert(e.isReadable,"Invalid bitfield access:" ~ name ~ " is not readable");
        /// 判定位域宽度大于0 且 小于等于寄存器宽度
        static assert(e.width >0 && e.width <= T.sizeof * 8,"Invalid bitfield width:" ~ name);

        static if(e.width == 1)
        {
            return this._register[e.offset];
        }
        else
        {

            //import core.volatile:volatileLoad;
            // 最小访问偏移
            // 起始字节位置偏移 
            enum startOffset = (e.offset / (ubyte.sizeof * 8));
            // 读写位偏移
            enum rwOffset = (e.offset % (ubyte.sizeof * 8));
            // 读写掩码偏移
            enum rwMaskOffset = (e.fieldMask >>> (startOffset * ubyte.sizeof * 8));

            // 确定访问类型
            alias getType = getAvailableType!(rwOffset + e.width);


            
            getType* nptr = cast(getType*)((&this._sharedValue) + startOffset);
            auto r = atomicLoad(cast(shared)*nptr);
            return (r & rwMaskOffset) >> rwOffset; 
            
            /*
            T ret = (r & rwMaskOffset) >> rwOffset;
            return ret;
            */
            /*
            pragma(msg,"FUN:",name);
            pragma(msg,"offset:",rwOffset);
            pragma(msg,"rtypesize:",getType.sizeof);
            pragma(msg,"rtypesize:",e.width);
            pragma(msg,"\n");
            */

        }
    }

    /// 重载赋值操作
    pragma(inline,true)
    void opAssign(T v) => (atomicStore(this._sharedValue,v));

    pragma(inline,true)
    void opAssign(ref typeof(this) v) => (atomicStore(this._sharedValue,cast(T)v));
    
    /// 重载 cast
    T opCast() const => (atomicLoad(this._sharedValue));


}

/**
* 获取可用类型
*/
private
template getAvailableType(size_t size)
{
    static if(size <= 8)
        alias getAvailableType = ubyte; 
    else static if(size <= 16)
        alias getAvailableType = ushort;
    else static if(size <= 32)
        alias getAvailableType = uint;
    else
        static assert(false," size > 32");
}



/**
* 位访问权限
*/
enum Access
{
    NO  = 0,
    /// 可读
    RO  = 1u << 0,
    /// 可写
    WO  = 1u << 1,
    /// 可读写
    RW  = RO | WO,
}



/**
* 位域定义模板
*/
@nogc nothrow
struct BitField(T = uint) 
{
    immutable
    {
        /// 位域偏移
        size_t offset;
        /// 位域宽度
        size_t width;
        /// 位域访问权限
        Access access;
    }
    ///Returns: 位域掩码
    @property T fieldMask() const
        => makeBitField(offset,width);
    ///Returns: 位域写掩码
    @property T fieldWriteMask() const
        => ((access & Access.WO) == Access.WO)? fieldMask : 0;
    ///Returns: 位域读掩码
    @property T fieldReadMask() const
        => ((access & Access.RO) == Access.RO)? fieldMask : 0;
    ///Returns: 位域读写掩码
    @property T fieldRWMask() const
        => ((access & Access.RW) == Access.RW)? fieldMask : 0;
    ///Returns: 位域可读
    @property bool isWriteable() const
        => (access & Access.WO) == Access.WO;
    ///Returns: 位域可写
    @property bool isReadable() const
        => (access & Access.RO) == Access.RO;
    ///Returns: 位域可读写
    @property bool isReadWriteable() const
        => (access & Access.RW) == Access.RW;

    this(size_t moffset, size_t mwidth, Access maccess)
    {
        this.offset = moffset;
        this.width = mwidth;
        this.access = maccess;
    }
}

/**
* 组合读权限
*/
template readAccess(E)
if(is(E == enum))
{
    enum allEnum = EnumMembers!E;
    enum Fun(alias r) = r.fieldReadMask;
    enum readAccess = combineOr!(staticMap!(Fun,allEnum));
}

/**
* 组合写权限
*/
template writeAccess(E)
if(is(E == enum))
{
    enum allEnum = EnumMembers!E;
    enum Fun(alias w) = w.fieldWriteMask;
    enum writeAccess = combineOr!(staticMap!(Fun,allEnum));    
}

